#include <stdafx.h>
#include "OgreWidget.h"
 
/*OgreWidget::OgreWidget(QWidget *parent)
 : QWidget(parent)
 {
 //ui.setupUi(this);
 }*/
OgreWidgetListener::OgreWidgetListener( OgreWidget * widget )
	: mWidget(widget)
{}
OgreWidgetListener::~OgreWidgetListener()
{}

void OgreWidgetListener::setWidget( OgreWidget* w ){ mWidget = w; }
OgreWidget* OgreWidgetListener::widget() const{ return mWidget; }

void OgreWidgetListener::mouseMoved( const QPoint& pos, Qt::MouseButtons )
{}
void OgreWidgetListener::mousePressed( const QPoint& pos, Qt::MouseButtons )
{}
void OgreWidgetListener::mouseReleased( const QPoint& pos, Qt::MouseButtons )
{}
void OgreWidgetListener::mouseDragged( MouseDrag & drag )
{}
void OgreWidgetListener::mouseDragging( MouseDrag & drag )
{}


void OgreWidgetListener::createScene( )
{}
void OgreWidgetListener::destroyScene( )
{}
bool OgreWidgetListener::frameRenderingQueued( const Ogre::FrameEvent& evt )
{return true;}
 
OgreWidget::OgreWidget(QWidget *parent, QPlainTextEdit* log_edit)
:QWidget(parent), 
mogreRoot(0), mogreSceneManager(0), mogreRenderWindow(0), mogreViewport(0),
mogreCamera(0), mCamMgr(0)
{
    //setAttribute(Qt::WA_OpaquePaintEvent);
	setAttribute(Qt::WA_NoSystemBackground);
    setAttribute(Qt::WA_PaintOnScreen);
    setMinimumSize(240,240);
	setMouseTracking( true );
}
 
OgreWidget::~OgreWidget()
{
	emit ogreShutdown();
	
    if(mogreRoot)
    {
		for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
			if( (*i) )
			{
				mogreRoot->removeFrameListener( (*i) );
				(*i)->destroyScene();
			}
		mListeners.clear();
	}
 	if( mCamMgr ) delete mCamMgr, mCamMgr = NULL;
	if(mogreRenderWindow)
	{
		mogreRenderWindow->removeAllViewports();
	}
	if(mogreRoot)
	{
        mogreRoot->detachRenderTarget(mogreRenderWindow);
 
        if(mogreSceneManager)
        {
            mogreRoot->destroySceneManager(mogreSceneManager);
        }
 
		delete mogreRoot, mogreRoot = NULL;
    }
}
void OgreWidget::mouseDragging( MouseDrag & drag )
{
	for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
		if( (*i) )
		{
			(*i)->mouseDragging(drag);
		}
}
void OgreWidget::mouseDragged( MouseDrag & drag )
{	
	for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
		if( (*i) )
		{
			(*i)->mouseDragged(drag);
		}
}
void OgreWidget::mousePressEvent( QMouseEvent*evt )
{
	QPoint p(evt->pos());
	Qt::MouseButtons mb(evt->buttons());

	mDrag.mousePressed( p, mb );
	for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
		if( (*i) )
		{
			(*i)->mousePressed(p,mb);
		}
}
void OgreWidget::mouseMoveEvent( QMouseEvent*evt )
{
	QPoint p(evt->pos());
	Qt::MouseButtons mb(evt->buttons());
	if( mDrag.Dragging )
		mouseDragging( mDrag );
	mDrag.mouseMoved(p,mb);
	for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
		if( (*i) )
		{
			(*i)->mouseMoved(p,mb);
		}
}
void OgreWidget::mouseReleaseEvent( QMouseEvent*evt )
{
	QPoint p(evt->pos());
	Qt::MouseButtons mb(evt->buttons());
	if( mDrag.Dragging )
		mouseDragged( mDrag );
	mDrag.mouseReleased(p,mb);
	for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
		if( (*i) )
		{
			(*i)->mouseReleased(p,mb);
		}
}
 
void OgreWidget::setBackgroundColor(QColor c)
{
    if(mogreViewport)
    {
        Ogre::ColourValue mogreColour;
        mogreColour.setAsARGB(c.rgba());
        mogreViewport->setBackgroundColour(mogreColour);
    }
}

void OgreWidget::messageLogged( const Ogre::String& message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &log_name )
{
	//if( mLogEdit ) mLogEdit->appendPlainText( message.c_str() );
}

void OgreWidget::moveEvent(QMoveEvent *e)
{
    QWidget::moveEvent(e);
 
    if(e->isAccepted() && mogreRenderWindow)
    {
        mogreRenderWindow->windowMovedOrResized();
        update();
    }
}
 
QPaintEngine *OgreWidget:: paintEngine() const
{
    return 0;
}
 
void OgreWidget::paintEvent(QPaintEvent *e)
{
//    mogreRoot->_fireFrameStarted();
//    mogreRenderWindow->update();
//    mogreRoot->_fireFrameEnded();
 
//	mogreRoot->renderOneFrame();
//	mogreRenderWindow->update();
 
//    e->accept();
	if( !mogreRoot ) return;
	if( !mogreRenderWindow ) return;
	try
	{
		mogreRoot->_fireFrameStarted();
		mogreRenderWindow->update();
		mogreRoot->_fireFrameRenderingQueued();
		mogreRoot->_fireFrameEnded();
		
		update();
	}
	catch( std::exception& e )
	{
		if( QMessageBox::question(this, "Rendering Error", (std::string("cannot render frame because: ")+e.what()).c_str(), QMessageBox::Retry|QMessageBox::Abort) == QMessageBox::Retry )
		{
		}
		else
		{
			throw;
		}
	}
	catch( ... )
	{
		if( QMessageBox::question(this, "Rendering Error", "Unknown error while rendering frame", QMessageBox::Retry|QMessageBox::Abort) == QMessageBox::Retry )
		{
		}
		else
		{
			throw;
		}
	}
}
 
void OgreWidget::resizeEvent(QResizeEvent *e)
{
    QWidget::resizeEvent(e);
 
    if(e->isAccepted()&&e)
    {
        const QSize &newSize = e->size();
        if(mogreRenderWindow)
        {
            mogreRenderWindow->resize(newSize.width(), newSize.height());
            mogreRenderWindow->windowMovedOrResized();
        }
        if(mogreCamera)
        {
            Ogre::Real aspectRatio = Ogre::Real(newSize.width()) / Ogre::Real(newSize.height());
            mogreCamera->setAspectRatio(aspectRatio);
        }
    }
}
 
void OgreWidget::showEvent(QShowEvent *e)
{
    if(!mogreRoot)
    {
		try
		{
			initOgreSystem();
			QWidget::showEvent(e);
			for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
				if( (*i) ){
					(*i)->createScene();
					mogreRoot->addFrameListener( (*i) );
				}
		}
		catch( std::exception &e )
		{
			QMessageBox::warning( parentWidget(), "Initialisation Error", e.what() );
		}
    } 
}
 
void OgreWidget::initOgreSystem()
{
	try
	{
#ifndef NDEBUG
		mogreRoot = new Ogre::Root( "plugins_d.cfg", "ogre_d.cfg", "ogre_d.log" );
#else
		mogreRoot = new Ogre::Root( "plugins.cfg", "ogre.cfg", "ogre.log" );
#endif

		//-------------------------------------------------------------------------------------
		// setup resources
		// Load resource paths from config file
		Ogre::ConfigFile cf;
#ifndef NDEBUG
		cf.load("resources_d.cfg");
#else
		cf.load("resources.cfg");
#endif

		// Go through all sections & settings in the file
		Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

		Ogre::String secName, typeName, archName;
		while (seci.hasMoreElements())
		{
			secName = seci.peekNextKey();
			Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
			Ogre::ConfigFile::SettingsMultiMap::iterator i;
			for (i = settings->begin(); i != settings->end(); ++i)
			{
				typeName = i->first;
				archName = i->second;
				Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
					archName, typeName, secName);
			}
		}
	}
	catch( std::exception & e )
	{
		throw std::runtime_error(std::string("Cannot initialise Ogre::Root because: ")+e.what());
	}
	try
	{
		Ogre::RenderSystem *renderSystem = mogreRoot->getRenderSystemByName("OpenGL Rendering Subsystem");
		mogreRoot->setRenderSystem(renderSystem);
		mogreRoot->initialise(false);
	}
	catch( std::exception & e )
	{
		throw std::runtime_error(std::string("Cannot initialise Ogre::RenderSystem because: ")+e.what());
	}
	
	try
	{
		mogreSceneManager = mogreRoot->createSceneManager(Ogre::ST_GENERIC);
 
		Ogre::NameValuePairList viewConfig;
		Ogre::String widgetHandle;
 
	#ifdef Q_WS_WIN
		widgetHandle = Ogre::StringConverter::toString((size_t)((HWND)winId()));
	#else
		QWidget *q_parent = dynamic_cast <QWidget *> (parent());
		QX11Info xInfo = x11Info();

		widgetHandle = Ogre::StringConverter::toString ((unsigned long)xInfo.display()) +
			":" + Ogre::StringConverter::toString ((unsigned int)xInfo.screen()) +
			":" + Ogre::StringConverter::toString ((unsigned long)q_parent->winId());
	#endif
		viewConfig["externalWindowHandle"] = widgetHandle;
 
	#if defined(Q_WS_WIN)
		//positive integer for W32 (HWND handle) - According to Ogre Docs
		widgetHandle = Ogre::StringConverter::toString((unsigned int)(winId()));
		viewConfig["externalWindowHandle"] = widgetHandle;

	#endif
 
	#if defined(Q_WS_X11)
			//poslong:posint:poslong:poslong (display*:screen:windowHandle:XVisualInfo*) for GLX - According to Ogre Docs
 
		QWidget *q_parent = dynamic_cast <QWidget *> (parent());
		QX11Info info = x11Info();
			widgetHandle  = Ogre::StringConverter::toString((unsigned long)info.display());
			widgetHandle += ":";
			widgetHandle += Ogre::StringConverter::toString((unsigned int)info.screen());
			widgetHandle += ":";
			widgetHandle += Ogre::StringConverter::toString((unsigned long)q_parent->winId());
			//widgethandle += ":";
			//widgethandle += Ogre::StringConverter::toString((unsigned long)(info.visual()));
 
			viewConfig["parentWindowHandle"] = widgetHandle;
	#endif
 
 
	#if defined(Q_WS_MAC)

		widgetHandle = Ogre::StringConverter::toString((unsigned int)(winId()));
		viewConfig["macAPI"] = "cocoa";
		viewConfig["macAPICocoaUseNSView"] = "true";

	#endif
		mogreRenderWindow = mogreRoot->createRenderWindow("Ogre rendering window",
			width(), height(), false, &viewConfig);
	}
	catch( std::exception & e )
	{
		throw std::runtime_error(std::string("Cannot initialise Ogre::RenderWindow because: ")+e.what());
	}
	try
	{
		Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
		mogreCamera = mogreSceneManager->createCamera("myCamera");
 
		mogreViewport = mogreRenderWindow->addViewport(mogreCamera);
		mogreViewport->setBackgroundColour(Ogre::ColourValue(0,0,0));
		mogreCamera->setAspectRatio(Ogre::Real(width()) / Ogre::Real(height()));
	
		mCamMgr = new Lit::CameraController( mogreCamera, 50 );
		mogreCamera->setPosition( 100, 5, 100 );
		mogreCamera->lookAt( 0,0,0 );
		mogreCamera->setNearClipDistance( 0.01 );
	}
	catch( std::exception & e )
	{
		throw std::runtime_error(std::string("Cannot create scene because: ")+e.what());
	}

	emit ogreInitialise();
}